package code.generate;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.SQLExec;

import code.generate.bean.Column;
import code.generate.bean.Table;
import code.generate.singleton.ConfigSingleton;

import com.google.common.base.CaseFormat;

import freemarker.template.TemplateException;

/**
 * @author "Jiance Qin"
 * 
 * @date 2014年7月15日
 * 
 * @time 上午11:17:53
 * 
 * @desc
 * 
 */
public class Main {

	public static final String catalog = null;
	public static final String schema = null;

	private static ConfigSingleton configSingleton = ConfigSingleton.getInstance();

	public static void main(String[] args) {

		Connection conn = null;
		try {
			System.out.println("======================== Database connections ===========================");
			String dbDriverName = configSingleton.getProperties()
					.getProperty("databaseDriver", "com.mysql.jdbc.Driver");
			String dbConnectionName = configSingleton.getProperties().getProperty("databaseConnection");
			if (StringUtils.isBlank(dbConnectionName)) {
				System.out.println("请先配置数据库连接: databaseConnection");
				return;
			}
			String dbName = configSingleton.getProperties().getProperty("databaseName");
			if (StringUtils.isBlank(dbName)) {
				System.out.println("请先配置数据库名: databaseName");
				return;
			}
			String username = configSingleton.getProperties().getProperty("databaseUsername");
			if (StringUtils.isBlank(username)) {
				System.out.println("请先配置数据库连接用户: databaseUsername");
				return;
			}
			String password = configSingleton.getProperties().getProperty("databasePassword");
			if (StringUtils.isBlank(password)) {
				System.out.println("请先配置数据库连接密码: databasePassword");
				return;
			}
			Class.forName(dbDriverName).newInstance();
			StringBuilder connectionUrl = new StringBuilder().append(dbConnectionName).append("/").append(dbName)
					.append("?user=").append(username).append("&password=").append(password);
			System.out.println("Database connection url is: " + connectionUrl.toString());
			conn = DriverManager.getConnection(connectionUrl.toString());
			DatabaseMetaData databaseMetaData = conn.getMetaData();
			ResultSet resultSet = databaseMetaData.getTables(catalog, schema, null, new String[] { "TABLE", "VIEW" });

			Generate generate = new Generate();
			while (resultSet.next()) {
				System.out.println("======================== Parse table ===========================");
				String tableName = resultSet.getString("TABLE_NAME");
				if (generate.hasIncludeConfig()) {
					if (!generate.isIncludeTable(tableName)) {
						System.out.println("Exclude table: " + tableName);
						continue;
					}
				} else {
					if (generate.isExcludeTable(tableName)) {
						System.out.println("Exclude table: " + tableName);
						continue;
					}
				}
				Table table = new Table();
				table.setDbName(tableName);
				System.out.println(tableName);
				ResultSet pkrs = databaseMetaData.getPrimaryKeys(catalog, schema, tableName);
				// TODO 只考虑单一列做主键
				String pkName = null;
				if (pkrs.next()) {
					pkName = pkrs.getString("COLUMN_NAME");
					System.out.println("Primary key: " + pkName);
				} else {
					System.out.println(tableName + " do not has a primariy key, skip this table.");
					continue;
				}
				ResultSet rs = databaseMetaData.getColumns(catalog, null, tableName, null);
				List<Column> columns = new ArrayList<Column>();
				List<Column> commonColumns = new ArrayList<Column>();
				while (rs.next()) {
					String columnName = rs.getString("COLUMN_NAME");// 列名
					int dataType = rs.getInt("DATA_TYPE");// 字段数据类型(对应java.sql.Types中的常量)
					String typeName = rs.getString("TYPE_NAME");// 字段类型名称(例如：VACHAR2)
					System.out.println(columnName + ":" + dataType + ":" + typeName);
					Column column = new Column();
					column.setDbName(columnName);
					column.setName(StringUtils.capitalize(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,
							columnName)));
					column.setTypeId(dataType);
					column.setDbType(typeName);
					columns.add(column);
					if (StringUtils.equals(pkName, columnName)) {
						table.setPrimaryKey(column);
					} else {
						commonColumns.add(column);
					}
				}
				table.setColumns(columns);
				table.setCommonColumns(commonColumns);
				// 生成和数据库表有关的文件
				generate.generate(table);
			}
			if (generate.isAddMode()) {
				//如果是增量生成模式，则一次性资源文件只生成需要覆盖的两个文件
				generate.generateAddSingle();
				return;
			}
			// 一次性资源文件
			System.out.println("======================== Generate single ===========================");
			generate.generateSingle();
			// 建op数据库
			if (StringUtils.isNotBlank(Generate.opSqlFile) && generate.needCreateOpDb()) {
				System.out.println("======================== Create op database ===========================");
				SQLExec sqlExec = new SQLExec();
				// 设置数据库参数
				sqlExec.setDriver(dbDriverName);
				StringBuilder url = new StringBuilder().append(dbConnectionName).append("/").append(dbName);
				sqlExec.setUrl(url.toString());
				sqlExec.setUserid(username);
				sqlExec.setPassword(password);
				File file = new File(Generate.opSqlFile);
				sqlExec.setSrc(file);
				sqlExec.setPrint(true); // 设置是否输出
				// 输出到文件 sql.out 中；不设置该属性，默认输出到控制台
				// sqlExec.setOutput(new File("d:/script/sql.out"));
				sqlExec.setProject(new Project()); // 要指定这个属性，不然会出错
				sqlExec.execute();
			}
			
		} catch (SQLException ex) {
			// handle any errors
			ex.printStackTrace();
			System.out.println("SQLException: " + ex.getMessage());
			System.out.println("SQLState: " + ex.getSQLState());
			System.out.println("VendorError: " + ex.getErrorCode());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
	}
}
